package grpc

import (
	"fmt"
	"gitee.com/cjf8134/bbs_serve/internal/pkg/utils/netutil"
	consulApi "github.com/hashicorp/consul/api"
	"github.com/pkg/errors"
	"github.com/spf13/viper"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"net"
)

// ServerOptions grpc server option
type ServerOptions struct {
	Port        int
	EtcdAddr    string
	ServiceName string
	Ttl         int64
}

func NewServerOptions(v *viper.Viper, logger *zap.Logger) (*ServerOptions, error) {
	var (
		err error
		o   = new(ServerOptions)
	)

	if err = v.UnmarshalKey("grpc", o); err != nil {
		return nil, errors.Wrap(err, "unmarshal grpc server option error")
	}

	logger.Info("load grpc options success", zap.Any("grpc options", o))

	return o, nil
}

type Server struct {
	o         *ServerOptions
	app       string
	host      string
	port      int
	logger    *zap.Logger
	server    *grpc.Server
	consulCli *consulApi.Client
}

type InitServers func(*grpc.Server)

func NewServer(o *ServerOptions, logger *zap.Logger, init InitServers) (*Server, error) {
	// initialize grpc server
	var gs *grpc.Server
	logger = logger.With(zap.String("type", "grpc"))
	gs = grpc.NewServer()
	init(gs)

	return &Server{
		o:      o,
		logger: logger.With(zap.String("type", "grpc.Server")),
		server: gs,
	}, nil
}

func (s *Server) Application(name string) {
	s.app = name
}

func (s *Server) Start() error {
	s.port = s.o.Port
	if s.port == 0 {
		s.port = netutil.GetAvailablePort()
	}

	addr := fmt.Sprintf("%s:%d", s.host, s.port)

	s.logger.Info("grpc server starting ...", zap.String("addr", addr))

	go func() {
		lis, err := net.Listen("tcp", addr)
		if err != nil {
			s.logger.Fatal("failed to listen: %v", zap.Error(err))
		}

		if err := s.server.Serve(lis); err != nil {
			s.logger.Fatal("failed to serve: %v", zap.Error(err))
		}
	}()

	return nil
}

func (s *Server) Stop() error {
	s.logger.Info("grpc server stopping ...")
	s.server.GracefulStop()
	return nil
}
